package com.imyuanma.qingyun.lowcode.util;

import com.imyuanma.qingyun.common.exception.Exceptions;
import com.imyuanma.qingyun.common.util.DateUtil;
import com.imyuanma.qingyun.common.util.StringUtil;
import com.imyuanma.qingyun.interfaces.common.model.enums.EYesOrNoEnum;
import com.imyuanma.qingyun.lowcode.model.bo.GenerateFieldBO;
import com.imyuanma.qingyun.lowcode.model.bo.GenerateMainBO;
import com.imyuanma.qingyun.lowcode.model.bo.SearchConditionItem;
import com.imyuanma.qingyun.lowcode.model.enums.ELcpQueryConditionTypeEnum;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 代码生成序列化工具类
 *
 * @author wangjy
 * @date 2022/06/27 23:20:32
 */
@Slf4j
public class GenerateSerializeUtil {
    /**
     * 代码模板目录路径
     */
    public static final String CODE_TEMPLATE_DIR_PATH = "/com/imyuanma/qingyun/lowcode/template/v4/";

    /**
     * 构造代码生成模板参数
     *
     * @param generateMainBO 代码生成配置
     * @return
     */
    public static Map<String, Object> buildCodeTemplateParam(GenerateMainBO generateMainBO) {
        // 首字母小写的类名
        String classNameLowerFirst = String.valueOf(generateMainBO.getClassName().charAt(0)).toLowerCase() + generateMainBO.getClassName().substring(1);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("model", generateMainBO);
        map.put("nowTimeStr", DateUtil.dateFormat(new Date()));
        map.put("modelClassPath", String.format("%s.%s.model.%s", generateMainBO.getPackagePath(), generateMainBO.getModuleName(), generateMainBO.getClassName()));
        // 主键
        GenerateFieldBO pk = generateMainBO.findPrimaryKeyField();
        map.put("primaryKeyJavaType", pk != null ? pk.getFieldJavaType() : "Long");
        map.put("primaryKeyJavaName", pk != null ? pk.getFieldJavaName() : "id");
        map.put("primaryKeyColumnName", pk != null ? pk.getFieldDbName() : "id");
        // 视图页字段排序
        List<GenerateFieldBO> listFields = generateMainBO.getFieldList().stream().sorted((o1, o2) -> {
            long a = o1.getListColumnOrder() != null ? o1.getListColumnOrder() : o1.getId();
            long b = o2.getListColumnOrder() != null ? o2.getListColumnOrder() : o2.getId();
            return (int) (a - b);
        }).collect(Collectors.toList());
        map.put("listFields", listFields);
        List<GenerateFieldBO> formFields = generateMainBO.getFieldList().stream().sorted((o1, o2) -> {
            long a = o1.getFormColumnOrder() != null ? o1.getFormColumnOrder() : o1.getId();
            long b = o2.getFormColumnOrder() != null ? o2.getFormColumnOrder() : o2.getId();
            return (int) (a - b);
        }).collect(Collectors.toList());
        map.put("formFields", formFields);

//        List<GenerateFieldBO> conditionFields = generateMainBO.getFieldList().stream()
//                .filter(item -> item.getListQueryCondition() != null && !ELcpQueryConditionTypeEnum.NO.getType().equals(item.getListQueryCondition()))
//                .sorted((o1, o2) -> {
//                    long a = o1.getFormColumnOrder() != null ? o1.getFormColumnOrder() : o1.getId();
//                    long b = o2.getFormColumnOrder() != null ? o2.getFormColumnOrder() : o2.getId();
//                    return (int) (a - b);
//                })
//                .collect(Collectors.toList());
        Map<String, GenerateFieldBO> fieldMap = generateMainBO.getFieldList().stream().collect(Collectors.toMap(GenerateFieldBO::getFieldJavaName, item -> item));
        // 搜索条件字段
        List<GenerateFieldBO> conditionFields = new ArrayList<>();
        for (SearchConditionItem conditionItem : generateMainBO.getExtInfo().getSearchConditionList()) {
            GenerateFieldBO fieldBO = fieldMap.get(conditionItem.getFieldJavaName());
            if (fieldBO != null) {
                conditionFields.add(fieldBO);
            }
        }
        map.put("conditionFields", conditionFields);
        // 页面相关url
        // 在线url前缀
        String onlineUrlPrefix = "/lowcode/online/api";
        // 生成代码url前缀
        String codeUrlPrefix = StringUtil.getDefaultValue(generateMainBO.getExtInfo().getWebDomain(), "/") + generateMainBO.getModuleName() + "/" + classNameLowerFirst;
        map.put("tableDataUrl", StringUtil.getDefaultValue(generateMainBO.getExtInfo().getTableDataUrl(), EYesOrNoEnum.YES.getCode().equals(generateMainBO.getExtInfo().getOnlineCodeFlag()) ? String.format("%s/getPage/%s", onlineUrlPrefix, generateMainBO.getId()) : String.format("%s/getPage", codeUrlPrefix)));
        map.put("formQueryUrl", StringUtil.getDefaultValue(generateMainBO.getExtInfo().getDetailUrl(), EYesOrNoEnum.YES.getCode().equals(generateMainBO.getExtInfo().getOnlineCodeFlag()) ? String.format("%s/get/%s", onlineUrlPrefix, generateMainBO.getId()) : String.format("%s/get", codeUrlPrefix)));
        map.put("formInsertUrl", StringUtil.getDefaultValue(generateMainBO.getExtInfo().getInsertUrl(), EYesOrNoEnum.YES.getCode().equals(generateMainBO.getExtInfo().getOnlineCodeFlag()) ? String.format("%s/insert/%s", onlineUrlPrefix, generateMainBO.getId()) : String.format("%s/insert", codeUrlPrefix)));
        map.put("formUpdateUrl", StringUtil.getDefaultValue(generateMainBO.getExtInfo().getUpdateUrl(), EYesOrNoEnum.YES.getCode().equals(generateMainBO.getExtInfo().getOnlineCodeFlag()) ? String.format("%s/update/%s", onlineUrlPrefix, generateMainBO.getId()) : String.format("%s/update", codeUrlPrefix)));
        map.put("deleteUrl", StringUtil.getDefaultValue(generateMainBO.getExtInfo().getDeleteUrl(), EYesOrNoEnum.YES.getCode().equals(generateMainBO.getExtInfo().getOnlineCodeFlag()) ? String.format("%s/delete/%s", onlineUrlPrefix, generateMainBO.getId()) : String.format("%s/delete", codeUrlPrefix)));
        return map;
    }

    /**
     * 生成代码文件
     *
     * @param templatePath 模板路径,例如:/com/imyuanma/qingyun/lowcode/template/v4/
     * @param templateName 模板名称,例如:Model.ftl
     * @param fileFullPath 文件保存绝对路径,C:\qingYunCodeGenerate/src/main/java/com/imyuanma/qingyun/lcp/model/Test.java
     * @param param        代码生成参数
     */
    public static void generateFile(String templatePath, String templateName, String fileFullPath, Map<String, Object> param) {
        // 检查文件目录是否存在
        File dir = new File(fileFullPath.substring(0, fileFullPath.lastIndexOf("/")));
        // 不存在此目录,则创建
        if (!dir.exists()) {
            dir.mkdirs();
            log.info("[代码生成] 成功创建目录: {}", dir.getAbsolutePath());
        }
        // 模板
        Template template = buildTemplateForTemplateFile(templatePath, templateName);
        // 写入
        try {
            writeByTemplate(template, param, new OutputStreamWriter(new FileOutputStream(fileFullPath), "UTF-8"));
        } catch (UnsupportedEncodingException | FileNotFoundException e) {
            log.error("[代码生成] 创建文件输出流异常", e);
            throw Exceptions.baseException("创建文件输出流异常");
        }
    }


    /**
     * 生成代码文件
     *
     * @param templateName    模板名称,例如:Model.ftl
     * @param templateContent 模板内容
     * @param fileFullPath    文件保存绝对路径,C:\qingYunCodeGenerate/src/main/java/com/imyuanma/qingyun/lcp/model/Test.java
     * @param param           代码生成参数
     */
    public static void generateFileByTemplateString(String templateName, String templateContent, String fileFullPath, Map<String, Object> param) {
        // 检查文件目录是否存在
        File dir = new File(fileFullPath.substring(0, fileFullPath.lastIndexOf("/")));
        // 不存在此目录,则创建
        if (!dir.exists()) {
            dir.mkdirs();
            log.info("[代码生成] 成功创建目录: {}", dir.getAbsolutePath());
        }
        // 模板
        Template template = buildTemplateForString(templateName, templateContent);
        // 写入
        try {
            writeByTemplate(template, param, new OutputStreamWriter(new FileOutputStream(fileFullPath), "UTF-8"));
        } catch (UnsupportedEncodingException | FileNotFoundException e) {
            log.error("[代码生成] 创建文件输出流异常", e);
            throw Exceptions.baseException("创建文件输出流异常");
        }
    }

    /**
     * 生成html到response
     *
     * @param templatePath 模板路径,例如:/com/imyuanma/qingyun/lowcode/template/v4/
     * @param templateName 模板名称,例如:Model.ftl
     * @param param        代码生成参数
     * @param response     http响应
     */
    public static void generateHtmlResponse(String templatePath, String templateName, Map<String, Object> param, HttpServletResponse response) {
        // 模板
        Template template = buildTemplateForTemplateFile(templatePath, templateName);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");
        // 写入
        try {
            writeByTemplate(template, param, response.getWriter());
        } catch (IOException e) {
            log.error("[代码生成] 获取http输出流异常", e);
            throw Exceptions.baseException("获取http输出流异常");
        }

    }

    /**
     * 生成html到response
     *
     * @param templateName    模板名称,例如:Model.ftl
     * @param templateContent 模板内容
     * @param param           代码生成参数
     * @param response        http响应
     */
    public static void generateHtmlResponseByTemplateString(String templateName, String templateContent, Map<String, Object> param, HttpServletResponse response) {
        // 模板
        Template template = buildTemplateForString(templateName, templateContent);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");
        // 写入
        try {
            writeByTemplate(template, param, response.getWriter());
        } catch (IOException e) {
            log.error("[代码生成] 获取http输出流异常", e);
            throw Exceptions.baseException("获取http输出流异常");
        }

    }

    /**
     * 模板生成写入输出流
     *
     * @param template 模板
     * @param param    参数
     * @param writer   输出流
     */
    private static void writeByTemplate(Template template, Map<String, Object> param, Writer writer) {
        Writer fw = new BufferedWriter(writer);
        try {
            template.process(param, fw);
        } catch (TemplateException | IOException e) {
            log.error("[代码生成] 模板写入发生异常", e);
            throw Exceptions.baseException("模板生成写入失败");
        } finally {
            try {
                fw.flush();
                fw.close();
            } catch (IOException e) {
                log.error("[代码生成] 关闭输出流发生异常", e);
            }
        }
    }

    /**
     * 构造freemarker模板对象
     *
     * @param templatePath 模板路径,例如:/com/imyuanma/qingyun/lowcode/template/v4/
     * @param templateName 模板名称,例如:Model.ftl
     * @return
     * @throws IOException
     */
    private static Template buildTemplateForTemplateFile(String templatePath, String templateName) {
        Configuration configuration = new Configuration();
        configuration.setClassForTemplateLoading(GenerateSerializeUtil.class, templatePath);
        configuration.setDefaultEncoding("UTF-8");
        // 去掉数字逗号, 最多支持8位小数
        configuration.setNumberFormat("#.########");
        try {
            return configuration.getTemplate(templateName, "UTF-8");
        } catch (IOException e) {
            log.error("[代码生成] 创建模板对象失败", e);
            throw Exceptions.baseException("创建模板对象失败");
        }
    }

    /**
     * 构造freemarker模板对象
     *
     * @param templateName    模板名称,例如:Model.ftl
     * @param templateContent 模板内容
     * @return
     * @throws IOException
     */
    private static Template buildTemplateForString(String templateName, String templateContent) {
        Configuration configuration = new Configuration();
        configuration.setTemplateLoader(new StringTemplateLoader());
        configuration.setDefaultEncoding("UTF-8");
        // 去掉数字逗号, 最多支持8位小数
        configuration.setNumberFormat("#.########");
        try {
            return new Template(templateName, templateContent, configuration);
        } catch (IOException e) {
            log.error("[代码生成] 创建模板对象失败", e);
            throw Exceptions.baseException("创建模板对象失败");
        }
    }
}
